﻿/*
 * Cratos Server - Entity Access Layer (EAL)
 * Cratos EAL Collection class
 * 
 * Copyright (c) 2011 Draco
 * 
 * This file is part of Cratos.
 * 
 * Cratos is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * Cratos is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cratos.  If not, see <http://www.gnu.org/licenses/>.
 * 
 *       .==.        .==.
 *      //'^\\      //^'\\
 *     // ^ ^\(\__/)/^ ^^\\
 *    //^ ^^ ^/6  6\ ^^ ^ \\
 *   //^ ^^ ^/( .. )\^ ^ ^ \\
 *  // ^^ ^/\| V""V|/\^ ^ ^ \\
 * //^^/\/ /  '~~'  \ \/\^ ^ \\
 *  ------------------------------------
 *  HERE BE DRAGONS
 *  
 *  Johan Dercksen - johan@draco.co.za
 */
using System;
using System.Linq;
using System.Text;
using System.Data;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using Cratos.Tools.General;
using Cratos.Tools.DataObject;

namespace Cratos.Server.Entity
{
	/// <summary>
	/// Cratos EAL Collection class
	/// </summary>
	public class CratosEALCollection<T> : CratosEALBase, ICratosEntityCollectionAccess<T>, IDisposable where T : IDOCratos, new()
	{
		#region Class Constant declarations
		#endregion

		#region Class Event declarations
		#endregion

		#region Class Variable declarations
		private bool										m_IsDisposed;
		private IDOCratosCollection<T>	m_DOCollection;
		#endregion

		#region Class Constructors & Destructors
		/// <summary>
		/// Class Constructor
		/// </summary>
		public CratosEALCollection() : base() { }

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or
		/// resetting unmanaged resources.
		/// </summary>
		public new void Dispose() { Dispose(true); GC.SuppressFinalize(this); }

		/// <summary>
		/// Dispose method doing the disposing
		/// </summary>
		/// <param name="isDisposing"></param>
		protected new virtual void Dispose(bool isDisposing)
		{
			// Declare the required variables

			// Check to see if Dispose has already been called.
			if (!this.m_IsDisposed)
			{
				// Dispose the objects if required
				if (isDisposing)
				{
					// Dispose of the required objects
				}
			}

			// Set the disposed indicator
			this.m_IsDisposed = true;

			// Return to calling method
			return;
		}
		#endregion

		#region Class Properties
		#endregion

		#region Class Methods
		/// <summary>
		/// Entity Perform Action
		/// </summary>
		/// <returns>A Boolean indicating success of the method</returns>
		public bool PerformAction(IDOCratosCollection<T> cratosDOCollection)
		{
			// Declare the required variables
			bool returnValue = false;

			try
			{
				// Reset all the relevant values
				this.Errors.Clear();

				// Save the relevant values
				this.DataObject     = (T)cratosDOCollection.DataObject.Clone();
				this.m_DOCollection = cratosDOCollection;

				// Validate the Data Object
				if (cratosDOCollection.Validate())
				{
					// Setup the DB Connection
					if (this.SetupDBConnection())
					{
						// Handle the relevant List Action
						switch (cratosDOCollection.CollectionAction)
						{
							case DOCratosCollectionAction.Retrieve:
								// Perform the relevant action
								returnValue = this.PerformRetrieve();
								break;

							case DOCratosCollectionAction.Various:
								// Perform the relevant action
								returnValue = this.PerformVarious();
								break;

							default:
								throw new Exception("Cratos Data Object Collection Action not implemented");
						}
					}
				}
			}
			catch (Exception runtimeException)
			{
				// Create and setup a new error
				this.Errors.Add(new CratosError("CratosCollectionEAL.PerformAction", CratosLogSeverity.Exception,
																				"Runtime error ocurred", runtimeException));
			}
			finally
			{
			}

			// Return to calling method
			return (returnValue);
		}
		#endregion

		#region Class Support Methods
		private bool PerformRetrieve()
		{
			// Declare the required variables
			bool			returnValue = false;
			IDOCratos	newDO;

			try
			{
				// Set the default object values
				this.m_DOCollection.CollectionActionResult	= DOCratosActionResult.Error;
				this.m_DOCollection.RecordCount							= 0;
				this.m_DOCollection.PageCount								= 0;

				// Clear all pending Paramaters
				this.DBAccess.ClearParameters();

				#region Command Type and Text processing
				// Set the relevant Command Options
				this.DBAccess.DBActionType = this.DataObject.DBActionType;
				this.DBAccess.DBActionText = "";

				// Process all the Commands
				foreach (DOCratosActionAttribute currentAction in this.m_DOCollection.AllDOActions)
				{
					// Look for the Retrieve All Command
					if ((currentAction.CratosAction == DOCratosAction.Retrieve) && (currentAction.CratosActionType == this.DataObject.DBActionType))
					{
						this.DBAccess.DBActionText = currentAction.ActionString;
						break;
					}
				}

				// Make sure we have Action Text specified
				if (string.IsNullOrEmpty(this.DBAccess.DBActionText))
				{
					throw new Exception(string.Format("Action not specified ({0} - {1}", this.m_DOCollection.GetType(), this.m_DOCollection.CollectionAction));
				}
				#endregion

				#region Parameter Processing
				// Handle the Cache Retrieve
				if (this.m_DOCollection.IsCacheRetrieve)
				{
					// Process all the Columns
					foreach (DOCratosCollectionFilterAttribute currentFilter in this.DataObject.AllDOCollectionFilters)
					{
						// Make sure the Filter is for the current Action
						if (string.IsNullOrEmpty(currentFilter.CratosCustomActionName))
						{
							// Add the relevant parameter
							this.AddParameter(currentFilter.DBParameterName, currentFilter.DbType, null, false, false, false, SqlDbType.Variant);
						}
					}

					// Process the Paging options
					if (this.m_DOCollection.RestrictResultCount)
					{
						// Add the relevant SP Parameters
						this.AddParameter("@StartRecord", DbType.Int32,
															((this.m_DOCollection.CurrentPage - 1) * this.m_DOCollection.MaxNoRecords) + 1, false, false, false, SqlDbType.Variant);
						this.AddParameter("@EndRecord", DbType.Int32,
															(this.m_DOCollection.CurrentPage * this.m_DOCollection.MaxNoRecords), false, false, false, SqlDbType.Variant);
					}
				}
				else
				{
					// Process all the Columns
					foreach (DOCratosCollectionFilterAttribute currentFilter in this.DataObject.AllDOCollectionFilters)
					{
						// Make sure the Filter is for the current Action
						if (string.IsNullOrEmpty(currentFilter.CratosCustomActionName))
						{
							// Add the SP Parameter
							this.AddParameter(currentFilter.DBParameterName, currentFilter.DbType,
																this.DataObject.GetType().GetProperty(currentFilter.Property).GetValue(this.DataObject, null),
																currentFilter.IsMandatory, false, false, SqlDbType.Variant);
						}
					}

					// Process the Paging options
					if (this.m_DOCollection.RestrictResultCount)
					{
						// Add the relevant SP Parameters
						this.AddParameter("@StartRecord", DbType.Int32,
															((this.m_DOCollection.CurrentPage - 1) * this.m_DOCollection.MaxNoRecords) + 1, false, false, false, SqlDbType.Variant);
						this.AddParameter("@EndRecord", DbType.Int32,
															(this.m_DOCollection.CurrentPage * this.m_DOCollection.MaxNoRecords), false, false, false, SqlDbType.Variant);
					}
				}
				#endregion

				#region Execute the stored procedure and process results
				// Execute the stored procedure
				this.DBAccess.ExecuteReader();

				// Make sure we have data to work with
				if (this.DBAccess.ReaderHasRows)
				{
					// Handle all return values
					do
					{
						// Create and setup the new Data Object
						newDO = new T();

						// Populate the Data Object
						this.LoadDataFromDataReader(newDO);

						// Add the Data Object to the list
						this.m_DOCollection.Add(newDO);

						// Process the Paging options
						if (this.m_DOCollection.RestrictResultCount)
						{
							// Update the relevant data
							if (this.DBAccess.ReaderContainsField("NoOfRecords"))
							{
								this.m_DOCollection.RecordCount	= this.DBAccess.ReaderGetInt64Value("NoOfRecords");
								this.m_DOCollection.PageCount		= (this.m_DOCollection.RecordCount / this.m_DOCollection.MaxNoRecords) + 1;
							}
							else
							{
								this.m_DOCollection.RecordCount = -1;
								this.m_DOCollection.PageCount		= -1;
							}
						}
					} while (this.DBAccess.ReaderRead());

					#region Process Embedded Objects
					// Process all the Embedded Object Attributes
					foreach (DOCratosEmbeddedAttribute currentEmbeddedAttribute in this.DataObject.AllDOEmbedded)
					{
						// Process all the Objects in the collection
						foreach (T currentDOObject in this.m_DOCollection)
						{
							// Handle the List section
							if (currentEmbeddedAttribute.IsCollection)
							{
								// Process the Embedded Object
								this.ProcessEmbeddedCollection(DOCratosHelper.GetPropertyValue(this.DataObject, currentEmbeddedAttribute.Property),
																							 currentDOObject, currentEmbeddedAttribute);
							}
							else
							{
								// Process the Embedded Object
								this.ProcessEmbeddedObject((IDOCratos) DOCratosHelper.GetPropertyValue(this.DataObject, currentEmbeddedAttribute.Property),
																					 currentDOObject, currentEmbeddedAttribute);
							}
						}
					}
					#endregion

					// Update the List Action Result
					this.m_DOCollection.CollectionActionResult = DOCratosActionResult.Success;
				}
				else
				{
					// Set the no records found return value
					this.m_DOCollection.CollectionActionResult = DOCratosActionResult.NoRecords;
				}
				#endregion

				// Set the successful return value
				returnValue = (this.Errors.Count <= 0);
			}
			catch (Exception runtimeException)
			{
				// Create and setup a new error
				this.Errors.Add(new CratosError("CratosCollectionEAL.PerformAction", CratosLogSeverity.Exception,
																				"Runtime error ocurred", runtimeException));
			}

			// Return to calling method
			return (returnValue);
		}

		private bool PerformVarious()
		{
			// Declare the required variables
			bool returnValue = false;

			// Return to calling method
			return(returnValue);
		}
		#endregion
	}
}
